Skip to content

Audit overhaul: chain-of-custody citations, recommendations-first IA, applied tracker, grammar post-process - #41

Merged
slittycode merged 2 commits into
mainfrom
audit/ui-overhaul
May 14, 2026
Merged

Audit overhaul: chain-of-custody citations, recommendations-first IA, applied tracker, grammar post-process#41
slittycode merged 2 commits into
mainfrom
audit/ui-overhaul

Conversation

@slittycode

Copy link
Copy Markdown
Owner

End-to-end implementation of the design audit's prescription. The product's chain-of-custody promise — every Phase 2 recommendation traces back to the Phase 1 measurement that justifies it — is now visually first-class on every card, not a 9px monospace footnote on a single section.

What lands

Chain of custody (audit findings #2 + #3)

  • New CitationBlock primitive renders a GROUNDED IN block above every Mix Chain / Patches / Sonic Element card. Rows are humanized via a new userLabels.ts map (~50 curated entries + humanizeFieldPath fallback so no raw camelCase ever reaches the producer).
  • New phase1Picker.ts service walks dotted paths, formats values with unit-aware rules (bpm → "157 BPM", spectralBalance.highs → "+1.1 dB", rt60 → "2.04s", confidence fields → percent, etc.), and computes the worst-confidence band across cited paths.
  • The existing ConfidenceBandBadge four-band vocabulary (Solid scaffold / Workable draft / Rough sketch / Unreliable) now appears top-right of every card with citations whose fields have a *Confidence sibling in CONFIDENCE_PAIRS.
  • CONFIDENCE_PAIRS exported from phase2Validator.ts so the validator and citation block share one source of truth.
  • Retires GroundingBadgeList at the Track Layout site; segmentIndexes ride through a new extraRows prop so arrangement-segment citations land inside the same block as measurement citations.

Recommendations-first IA (audit finding #1)

  • MeasurementDashboard moved from the top of the results scroll to the bottom, wrapped in a single section-measurements anchor.
  • StickyNav's 9 section-meas-* pills collapse into one trailing Measurements entry. The 9 sub-sections inside the dashboard keep their ids and remain individually scrollable via direct hash links.
  • Section order: Style → [v2: Setup / Layout / Routing / Warp] → Audio Observations → Arrangement → Session → Stem Notes → Sonic → Mix Chain → Patches → Measurements.

Applied-recommendations tracker (audit findings #14 + #15)

  • New appliedRecommendations.ts service maintains a per-file applied-id set in localStorage, keyed by audio content SHA256 (so renaming the file doesn't reset progress).
  • Per-card checkbox affordance on Mix Chain + Patches cards. Section-header chips show N of M applied (suppressed when zero, so first views aren't littered).

Idle value-prop panel (audit finding #5)

  • New IdleValuePropPanel replaces the 200px NO SIGNAL DETECTED atmospheric canvas with a producer-readable explanation: "Drop a reference, get a measurement-cited rebuild plan for Live 12." + flow illustration + 3 honest pacing bullets (local measurement ~30s, AI interpretation typically 4–5 min, native devices only).

Header polish (audit #7, #9, #11)

  • CPU meter removed — browser-tab CPU is misleading during backend-subprocess analysis.
  • Local DSP Engine v1.6.0 eyebrow removed — was the 5th competing identity signal and wrapped to 3 lines at 375px on mobile.
  • Dense DAW Lab demoted from accent-chip to a quiet text link.

Engineering vocab cleanup (audit #8 + N3/N4/N5/N8)

  • New userLabels.ts map (used everywhere a field path could reach a user).
  • Export-button labels: JSON_DATADownload data, REPORT_MDDownload report.
  • FAMILY: NATIVE chip dropped from meta-badge rows (almost-always-the-same noise).
  • workflowStage prettified at the view-model layer: SOUND_DESIGNSound design, MIXMix.
  • AI Interpretation gated copy reworded: Developer kill-switch is off…AI interpretation isn't configured. Set GEMINI_API_KEY on the backend…; status badge INTERPRETATION CONFIG OFFNOT CONFIGURED, INTERPRETATION USER OFFOFF.

Patches group structure (audit follow-up)

  • New PatchGroupViewModel + buildPatchGroups mirrors Mix Chain's inferProcessingGroup heuristic. Producers see Drum / Bass / Synth / Mid / High-end / Master eyebrows instead of a flat 2-col grid.

Input Source collapse (audit N9)

  • Post-analysis, the Input Source panel collapses to a compact summary (filename + M:SS duration + ↺ Analyze new file + Adjust settings). Override resets at the start of each new analysis for predictability.

Progress panel primary readout (audit #6)

  • Stage diagnostic message promoted from text-[9px] text-secondary/50 footnote to the visual focus of the progress card.
  • Tone-aware fill (progress.tone: running / success / failed) so a failed 100% renders red instead of accent-orange (matches the FAILED stage badge below).

Phase 2 failure mode (audit N1)

  • Header subtitle derives from analysisRun.stages.interpretation.status instead of the hardcoded PHASE COMPLETE. No more "PHASE COMPLETE" while INTERPRET is still RUNNING or FAILED.
  • StickyNav Phase 2 pills (Sonic / Mix Chain / Patches) render disabled with a hover-reason ("Recommendations not produced this run") instead of silently dropping.
  • Retry button gates on error.retryable !== false; non-retryable failures (e.g., GEMINI_NOT_CONFIGURED) surface the error code inline with the full message in the tooltip.

Misc audit follow-ups

  • BPM reconciled across the exec card + Core Metrics tile (was 157 vs 156.6) — N2.
  • Signal Monitor STANDBY canvas hidden when audio isn't playing, freeing ~160px in the most common state (N7).
  • Device ChainSections in StickyNav (N10, less ambiguous with Ableton's own "device chain" meaning).
  • 🫧 → <AudioWaveform /> Lucide icon for BASS PROCESSING group eyebrow (#2 #13).
  • Toggle helper paragraphs in the Input Source panel switched from all-caps mono walls to sans-serif sentence case (fix: chunk Demucs inference and stop boot-time memory spikes #4 revised).

Phase 2 grammar post-process

The prompt instruction added in the prior round didn't take — Gemini still emitted 3rd-person singular forms after "by" in role/reason text ("by recreates", "by absorbs", "by shapes"). New server-side _apply_phase2_grammar_fixes rewrites these to gerunds in-place on:

  • mixAndMasterChain[].reason
  • abletonRecommendations[].{reason, advancedTip}
  • secretSauce.workflowSteps[].{instruction, measurementJustification}

Conservative regex (\bby \w{4,}s\b) + denylist guards against plural-noun false positives ("by samples", "by beats", "by buses" all stay intact).

Test coverage

  • UI: 46 test files / 540 tests pass (was 39 / 422 before the audit). Net +118 tests across new services (userLabels, phase1Picker, citationBlock, appliedRecommendations, formatTrackDuration, interpretationSubtitle, workflowStagePrettifier, analysisStatusProgress, idleValuePropPanel, phase2NavReason) plus DOM-level integration tests in analysisResultsUi.test.ts.
  • Backend: 16 new unit tests in test_phase2_grammar_fix.py covering _to_gerund, _fix_by_gerund_in_text, and _apply_phase2_grammar_fixes against the actual screenshot corpus. Full ASA backend suite 463 of 463 pass. One pre-existing unrelated failure in tests.test_url_ingest.MimeTypePickerTests.test_octet_stream_triggers_filename_fallback (system MIME registry returns audio/x-flac instead of expected audio/flac) predates this branch.
  • Build: npm run build clean. AnalysisResults chunk 247.95 KB / 60.36 KB gzip.

Visual verification

Playwright capture pass against a real 126s track (apps/backend/.runtime/artifacts/0a1e46db-…mp3). Phase 2 returned in 220s on the run. Screenshots in /tmp/asa-shots-audit-final/ confirm every audit surface:

  • Header has no CPU meter, no version eyebrow, no header wrap on 375×812.
  • GROUNDED IN block visible on every card.
  • Confidence pills emerge contextually (SOLID SCAFFOLD · 99% on Acid Bass; WORKABLE DRAFT · 78% on Supersaw Synth).
  • 2 OF 8 APPLIED chip after clicking two applied checkboxes; localStorage round-trip survived a page reload.
  • Patches grouped under 🥁 DRUM / 🌊 BASS / 🎹 SYNTH / 🧱 MASTER eyebrows.
  • Section order reads Style → Sonic → Mix Chain → Patches → Measurements.

The grammar post-process is unit-tested but not yet visually re-verified — the next Phase 2 run against the same fixture will show "by recreating" / "by shaping" / "by absorbing" instead of the 3rd-person singular forms.

Known limitations

  • The gerund rule is algorithmic, not dictionary-backed. Verbs requiring English consonant-doubling (control → controlling, submit → submitting, run → running) degrade to "controling" / "submiting". Still better than the original by controls. If observed in real Phase 2 output, drop a hand-mapped exception into the module rather than implement orthographic stress detection.
  • One pre-existing test failure on main (tests.test_url_ingest.MimeTypePickerTests.test_octet_stream_triggers_filename_fallback) is unaffected by this PR — audio/x-flac vs audio/flac MIME-registry mismatch from an earlier merged change.

Files

31 files changed, +3,511 / −403.

🤖 Generated with Claude Code

…rammar post-process

Implements the design audit's prescription end-to-end (findings #1#15 + N1/N2/N7/N9/N10
+ follow-ups). The product's chain-of-custody promise — every Phase 2 recommendation
traces back to the Phase 1 measurement that justifies it — is now visually first-class
on every card, not a 9px monospace footnote.

What ships:

* Chain-of-custody (findings #2 + #3). New CitationBlock primitive renders a structured
  "GROUNDED IN" block above every Mix Chain / Patches / Sonic Element card with
  humanized labels (FIELD_LABELS map, ~50 entries + humanizeFieldPath fallback) and a
  ConfidenceBandBadge pill computed from the worst confidence among cited fields.
  Also retires GroundingBadgeList at the Track Layout site; segmentIndexes ride
  through a new extraRows prop.

* Recommendations-first IA (#1). MeasurementDashboard moved to the bottom of the
  results scroll; StickyNav's 9 measurement pills collapse to one trailing
  "Measurements" entry. Producers hit Style → Sonic → Mix Chain → Patches → Session
  before the measurement evidence.

* Header polish (#7, #9, #11). CPU meter removed (browser-tab CPU is misleading
  during backend analysis), "Local DSP Engine v1.6.0" eyebrow removed (resolves
  mobile 3-line wrap), Dense DAW Lab demoted from accent chip to quiet text link.

* Engineering vocab cleanup (#8 + N3/N4/N5/N8). New userLabels.ts service translates
  field paths to producer-readable labels at every render. Button labels renamed
  (Download data / Download report). FAMILY: NATIVE chip dropped from meta-badge rows.
  workflowStage prettified at the view-model layer ("Sound design" not "SOUND_DESIGN").
  AI Interpretation gated copy reworded ("AI interpretation isn't configured…" not
  "Developer kill-switch is off").

* Applied-recommendations tracker (#14 + #15). Per-card checkbox affordance + section-
  header "N of M applied" chip + localStorage persistence keyed by audio content
  SHA256. Producer can rename their file without losing their progress.

* Idle value-prop panel (#5). Replaces the 200px "NO SIGNAL DETECTED" canvas with
  a producer-readable explanation of what ASA does, with honest pacing copy
  (4–5 min Phase 2 wait, not 2–5 min).

* Patches group structure. Mirrors Mix Chain's emoji-eyebrow grouping (Drums / Bass /
  Synth / Master) so producers can jump to the bass patch without scanning 8 cards.

* Input Source collapse (N9). Post-analysis, the Input Source panel collapses to a
  compact summary with filename + duration + "Analyze new file" + "Adjust settings".
  Frees the top of the page for the results the user came for.

* AnalysisStatusPanel primary readout (#6). Stage diagnostic message promoted from
  9px footnote to the visual focus of the progress card. Pre-existing tone-aware
  fill (running / success / failed) preserved.

* Phase 2 failure mode (N1). Header subtitle derives from interpretation stage status
  (no more "PHASE COMPLETE" while INTERPRET still RUNNING/FAILED). StickyNav Phase 2
  pills render disabled with hover-reason when sections didn't populate. Retry button
  gated on error.retryable; non-retryable failures surface the error code inline.

* Misc audit follow-ups: BPM reconciled across exec card + Core Metrics tile (N2);
  Signal Monitor STANDBY canvas hidden when audio isn't playing, freeing ~160px (N7);
  StickyNav label "Device Chain" → "Sections" (N10, less ambiguous with Ableton's own
  effects-routing meaning); BASS group icon swapped from 🫧 → Lucide AudioWaveform
  (#13); toggle helper paragraphs switched from all-caps mono walls to sans-serif
  sentence case (#4 revised).

* Phase 2 grammar post-process (audit final round). The prompt instruction added
  earlier didn't take — Gemini still emits "by recreates / by absorbs / by shapes"
  3rd-person singular forms after "by" in role/reason text. Server-side
  _apply_phase2_grammar_fixes rewrites these to gerunds in-place on
  mixAndMasterChain[].reason, abletonRecommendations[].{reason,advancedTip}, and
  secretSauce.workflowSteps[].{instruction,measurementJustification}. Conservative
  regex (\bby \w{4,}s\b) + denylist guards against plural-noun false positives.

Test coverage:

* UI: 46 test files / 540 tests pass (was 39/422 before the audit). New service tests:
  userLabels (21), phase1Picker (25), citationBlock (12), appliedRecommendations (16),
  formatTrackDuration (12), interpretationSubtitle (10), workflowStagePrettifier (8),
  analysisStatusProgress (6), idleValuePropPanel + phase2NavReason. New DOM tests in
  analysisResultsUi.test.ts cover Track Layout citation, applied-checkbox flow, mix-
  chain citation rendering.

* Backend: 16 new unit tests in test_phase2_grammar_fix.py cover _to_gerund,
  _fix_by_gerund_in_text, _apply_phase2_grammar_fixes including the actual
  screenshot corpus (recreates → recreating, shapes → shaping, matches → matching,
  etc.). Full suite: 463 of 463 ASA tests pass; 1 unrelated pre-existing failure
  in tests.test_url_ingest predates this branch.

Visual verification: Playwright capture pass against a real 126s track confirmed
every surface (15 screenshots in /tmp/asa-shots-audit-final/). Phase 2 returned in
220s; localStorage round-trip verified on applied-checkbox toggles.

Documented limitations:

* The gerund rule is algorithmic — verbs requiring consonant doubling (control →
  controlling, submit → submitting) degrade to "controling" / "submiting". Still
  better than "by controls". Drop a hand-mapped exception into the module if
  observed in real output.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@slittycode slittycode left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE

Summary

Large UI overhaul (31 files, +3511/−403) implementing the design audit's full prescription: structured chain-of-custody citations on every card, recommendations-first IA, applied-recommendations tracker, idle value-prop panel, progress card honesty fixes, and a server-side Phase 2 grammar post-process. Phase boundary is clean throughout. Test coverage is exemplary — every new service gets its own test file. The PR description's stated counts (540 frontend / 463 backend passing) are consistent with the test code I read.

Findings

Should fix

server_phase2.py, _fix_grammar_in_record (final return):

return record if updated or True else record  # always return; updated flag unused

or True makes this permanently truthy — the else record branch is dead and will confuse the next reader. Both branches return the same value so it's not a functional bug, but return record is what this should say. The comment already admits the flag is unused; finish the cleanup.

Worth considering

analysisResultsViewModel.ts, SONIC_ELEMENT_FIELD_PATHS: unknown Phase 2 element keys silently fall back to the harmonicContent citation set (['key', 'spectralBalance.mids', 'keyConfidence']). Wrong citations are worse than no citations — if Phase 2 ever emits a new key like vocalMelody, it will get harmonicContent's fields cited to the producer with no warning. A console.warn on the fallback path would make this loud enough to catch before it ships in a real run. Low-urgency.

Known limitations acknowledged (not flagging)

  • _to_gerund produces wrong gerunds for consonant-doubling verbs ("controling", "submiting"). Documented in both the PR description and the test file comment. The hand-mapped exception table is the right fix when it bites.
  • Regex only matches lowercase by"By recreates…" at sentence start is not rewritten. The PR notes role/reason fields don't open with "By" in practice. Acceptable.
  • _PHASE2_BY_NOUN_DENYLIST has "tracks" twice; Python frozenset deduplicates silently.

Test results

Could not execute locally (no vitest or Python venv in this sandbox). Test code I read is meaningful: screenshot corpus cases, denylist edge cases, null/empty guards, multi-rewrite strings, and the progress-card state machine (the primary fix for the "Analysis complete." lie at 100% while a stage is FAILED). Not coverage padding.

Backend 463/463 (PR-reported). Frontend 540/540, net +118 (PR-reported).

Phase boundary check

Clean.

_apply_phase2_grammar_fixes touches exactly: abletonRecommendations[].{reason, advancedTip}, mixAndMasterChain[].reason, secretSauce.workflowSteps[].{measurementJustification, instruction} — all Phase 2 output fields. Phase 1 data is never read, assigned, or re-derived by this function.

phase1Fields on view-model objects are string[] path references, not values. pickPhase1Value / formatCitedValue are pure read-only. CONFIDENCE_PAIRS export from phase2Validator.ts is a reference to an existing object, not a schema change.

Phase 1 contract unchanged. No diff to analyze.py, JSON_SCHEMA.md, or src/types/measurement.ts. The BPM display change in MeasurementDashboard.tsx (formatNumber(phase1.bpm, 1)Math.round(phase1.bpm)) is display-only; phase1.bpm is untouched.


Generated by Claude Code

CI surfaced 7 smoke test failures from this branch's audit changes that
hadn't been propagated to the smoke spec assertions:

* `tests/smoke/ui-details.spec.ts` × 3
  - `NO SIGNAL DETECTED` → `IdleValuePropPanel` (audit #5)
  - `JSON_DATA` / `REPORT_MD` button labels → `Download data` / `Download report`

* `tests/smoke/responsive-layout.spec.ts` × 4
  - `NO SIGNAL DETECTED` (×2) → `IdleValuePropPanel`
  - `CPU` text-presence checks removed; the two viewport-shape tests now
    assert just the model-selector responsive behavior (audit #11 retired
    the CPU meter; there's no element to assert)

* `tests/smoke/file-validation.spec.ts` × 1
  - `re-upload after results resets to file-selected state`: the test
    used `Remove File` (FileUpload component's affordance) to clear after
    results were visible. Post-N9 collapse, the Input Source panel
    replaces FileUpload with a compact summary card whose "↺ Analyze new
    file" button calls the same handleFileClear. Switched the test to
    target the new affordance.

Also updated the e2e exports spec for label consistency (not in the
failing CI job, but the same renames apply):

* `tests/e2e/phase1-exports.spec.ts`
  - `downloadTextArtifact(page, /JSON_DATA/i)` → `/Download data/i`
  - `downloadTextArtifact(page, /REPORT_MD/i)` → `/Download report/i`

Verified locally against the live stack: 45 of 46 smoke tests pass, 1
skipped (was unrelated). The previously-failing 7 are all green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@slittycode
slittycode merged commit a901996 into main May 14, 2026
2 checks passed
@slittycode
slittycode deleted the audit/ui-overhaul branch May 14, 2026 03:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant